# -*- coding=utf-8 -*-
# Created Time: 2016年08月31日 星期三 11时33分05秒
# File Name: kind.py

"""
测试类
"""

from utils.routes      import route
from utils             import mongo
from utils.errors      import err_param
from utils.lib_time    import dt_str_now
from utils.pages       import pages_count
from utils.lib_re      import format_add_0, regex_char_trans
from utils.lib_openpyxl import Excelib

from tornado.web       import RequestHandler
from service.base      import AuthHandler
from loog.mylog        import flogger as logger
from configure.config  import DB_NAME, COLL_CLASS, COLL_CLASSDV,\
                              COLL_CLASSPUSH, COLL_CLASSRES, COLL_CLASSLOG,\
                              COLL_SEED

from service.index.record import keep

from bson.objectid import ObjectId

import json, tornado, pymongo, datetime


# 测试类
@route(r'/class/add', name='class_add')
class ClassAddHandler(AuthHandler):
    """ 添加测试类

    类编号: 字母+自增值
        每次取出最大的值+1，再格式化 C0001
        取最大值问题，asyncmongo 没有提供 $max 操作
        用 find limit 取最大的一个
    """

    @tornado.web.asynchronous
    @tornado.gen.engine
    def post(self):

        try:
            token    = self.request.headers.get('TData',{})
            udict    = json.loads(self.request.body)
            name     = udict['name']
            remark   = udict['remark']
            created  = dt_str_now()
            username = token['n']
        except Exception, e:
            logger.exception(e)
            self.write(err_param)
            self.finish()
            return

        # 生成类编号
        try:
            sort = [['clsno', pymongo.DESCENDING]]
            cls = yield tornado.gen.Task(
                            mongo.find_all,
                            DB_NAME,
                            COLL_CLASS,
                            {},
                            limit=1,
                            sort=sort)
        except Exception, e:
            logger.exception(e)
            self.write({'code':500})
            self.finish()
            return

        if not cls:   # 创建第一个测试类
            no = format_add_0(1)
        else:
            no = format_add_0(int(cls[0]['clsno'][1:])+1)

        clsno = 'C' + no
        keyword = name + ' ' + clsno
        class_dict = {
            'uid'    :token['_id'],
            'name'   :name,
            'pushed' :0,            # 是否推送过(有无推送记录)
            'clsno'  :clsno,
            'created':created,
            'remark' :remark,
            'keyword':keyword
        }

        note = {
            'created' : dt_str_now(),
            'type'    :'添加类',
            'obj'     :'测试类',
            'username': username,
            'keyword' :'添加类测试类'
        }

        try:
            yield tornado.gen.Task(mongo.insert, DB_NAME, COLL_CLASS, class_dict)

            yield tornado.gen.Task(keep, note)
        except Exception, e:
            logger.exception(e)
            self.write({'code':500})
            self.finish()
            return

        self.write({'code':200})
        self.finish()


@route(r'/class/edit', name='class_edit')
class ClassEditHandler(AuthHandler):
    """ 编辑测试类

    只是编辑　名称＆描述
    """

    @tornado.web.asynchronous
    @tornado.gen.engine
    def post(self):

        try:
            udict    = json.loads(self.request.body)
            cid      = udict['id']
            name     = udict['name']
            remark   = udict['remark']
        except Exception, e:
            logger.exception(e)
            self.write(err_param)
            self.finish()
            return

        try:
            cls = yield tornado.gen.Task(
                            mongo.find_one,
                            DB_NAME,
                            COLL_CLASS,
                            {'_id':cid})
        except Exception, e:
            logger.exception(e)
            self.write({'code':500})
            self.finish()
            return

        keyword = name + ' ' + cls['clsno']
        class_dict = {
            'name'   :name,
            'remark' :remark,
            'keyword':keyword
        }

        try:
            yield tornado.gen.Task(
                            mongo.update,
                            DB_NAME,
                            COLL_CLASS,
                            {'_id':cid},
                            class_dict)
        except Exception, e:
            logger.exception(e)
            self.write({'code':500})
            self.finish()
            return

        self.write({'code':200})
        self.finish()


@route(r'/class/list', name='class_list')
class ClassListHandler(AuthHandler):
    """ 测试类 - 列表 """

    @tornado.web.asynchronous
    @tornado.gen.engine
    def post(self):
        try:
            udict    = json.loads(self.request.body)
            keyword  = udict.get('keyword')
            page     = udict.get('page',1)
            limit    = udict.get('page_size', 15)
        except Exception, e:
            logger.exception(e)
            self.write(err_param)
            self.finish()
            return

        skip  = (page-1)*limit
        query = {}
        if keyword:
            keyword = regex_char_trans(keyword)
            query.update({'keyword': {'$regex':keyword}})

        try:
            sort = [['created', pymongo.DESCENDING]]
            fields = {'uid':0,'keyword':0}
            seeds = yield tornado.gen.Task(
                                        mongo.find_all,
                                        DB_NAME,
                                        COLL_CLASS,
                                        query,
                                        skip=skip,
                                        limit=limit,
                                        sort=sort,
                                        fields=fields)
            count = yield tornado.gen.Task(
                                        mongo.count,
                                        DB_NAME,
                                        COLL_CLASS,
                                        sort,
                                        query)
        except Exception, e:
            logger.exception(e)
            self.write({'code':500})
            self.finish()
            return

        pages = pages_count(count, limit)
        self.write({
            'code':200,
            'page':page,
            'page_size':limit,
            'pages':pages,
            'data':seeds
        })
        self.finish()


@route(r'/class/detail', name='class_detail')
class ClassDetailHandler(AuthHandler):
    """ 测试类－详情 """

    @tornado.web.asynchronous
    @tornado.gen.engine
    def post(self):

        try:
            udict = json.loads(self.request.body)
            fid   = udict['id']
        except Exception, e:
            logger.exception(e)
            self.write(err_param)
            self.finish()
            return

        try:
            seed = yield tornado.gen.Task(
                                        mongo.find_one,
                                        DB_NAME,
                                        COLL_CLASS,
                                        {'_id':fid})
        except Exception, e:
            logger.exception(e)
            self.write({'code':500})
            self.finish()
            return

        self.write({
            'code':200,
            'data':seed
        })
        self.finish()


# 测试类子设备
@route(r'/classdv/detail', name='classdv_detail')
class ClassDvListHandler(AuthHandler):
    """ 测试类－子设备 - 列表 """

    @tornado.web.asynchronous
    @tornado.gen.engine
    def post(self):
        try:
            data  = {}
            udict = json.loads(self.request.body)
            cid   = udict['id']  # 测试类id
        except Exception, e:
            logger.exception(e)
            self.write(err_param)
            self.finish()
            return

        try:
            dv = yield tornado.gen.Task(
                                    mongo.find_one,
                                    DB_NAME,
                                    COLL_CLASSDV,
                                    {'classid':cid})
            if dv: data = dv
        except Exception, e:
            logger.exception(e)
            self.write({'code':500})
            self.finish()
            return

        self.write({
            'code':200,
            'data':data
        })
        self.finish()


@route(r'/classdv/save', name='classdv_save')
class ClassDvSaveHandler(AuthHandler):
    """ 保存测试类子设备数据

    Function:
        1. 子设备，什么功能都没有，只是存起来展示用

    Stuff:
        1. 每个 测试类 对应一条 记录
        2. 记录存在　  更新
        3. 记录不存在　新建

        4. 需要更新测试类中设备数量

        5. 需要冗余一份 sn 列表, 供其他方法调用
    """

    @tornado.web.asynchronous
    @tornado.gen.engine
    def post(self):

        try:
            udict = json.loads(self.request.body)
            cid   = udict['id']
            data  = udict['data']
        except Exception, e:
            logger.exception(e)
            self.write(err_param)
            self.finish()
            return

        # 处理上传的子设备数据 去掉空sn, 存一份 sn_list
        sn_list  = []
        res_data = []
        for i in data:
            if i.get('sn'):
                res_data.append(i)
                sn_list.append(i.get('sn'))

        query = {'classid':cid}
        class_dict = {
            'data_list':res_data,
            'sn_list'  :sn_list,
            'num'      :len(res_data)
        }

        try:
            # 插入或更新子设备数据
            yield tornado.gen.Task(
                            mongo.update,
                            DB_NAME,
                            COLL_CLASSDV,
                            query,
                            class_dict,
                            upsert=True)

            # 更新测试类中子设备的数量
            yield tornado.gen.Task(
                            mongo.update,
                            DB_NAME,
                            COLL_CLASS,
                            {'_id':cid},
                            {'devices':len(res_data)})
        except Exception, e:
            logger.exception(e)
            self.write({'code':500})
            self.finish()
            return

        self.write({'code':200})
        self.finish()


@route(r'/classdv/upload', name='classdv_upload')
class ClassDvUploadHandler(AuthHandler):
    """ 解析文件中子设备数据

    数据处理:
        1. 判重
        2. sn号 15 位
    """

    @tornado.web.asynchronous
    @tornado.gen.engine
    def post(self):

        try:
            file_metas = self.request.files['file'][0]
        except Exception, e:
            logger.exception(e)
            self.write(err_param)
            self.finish()
            return

        res   = []
        errs  = []
        echo  = []
        snset = set()

        body = file_metas['body']
        foo = body.split('\n')

        for b in foo:

            if not b: continue

            try:
                data = b.split(',')
                sn, remark = data[0], data[1]

                try:
                    remark = remark.decode('gb18030') # 处理windows上中文编码
                except:
                    pass

                if len(sn) != 15:
                    errs.append({'sn':sn,'remark':remark})
                    continue

                if sn not in snset:
                    res.append({'sn':sn,'remark':remark})
                    snset.add(sn)
                else:
                    echo.append({'sn':sn,'remark':remark})
            except Exception, e:
                logger.exception(e)

        self.write({
            'code':200,
            'data':{
                'sns' :res,
                'errs':errs,
                'echo':echo
            }
        })
        self.finish()


#@route(r'/classdv/upload/page', name='classdv_upload_page')
class ClassDvUploadHandler(tornado.web.RequestHandler):
    """ 解析文件中子设备数据 - 页面 """

    def get(self):
        self.render('manage/classdv.html')


# 测试类－推送
@route(r'/classpush/check/name', name='classpush_check_name')
class ClassPushCheckNameHandler(AuthHandler):
    """ 测试类－推送 - 检查推送名称

    检查:
        1. 推送名称同类内不能重复，所以需要这个异步检查名称的接口
    """

    @tornado.web.asynchronous
    @tornado.gen.engine
    def post(self):
        try:
            udict = json.loads(self.request.body)
            cid   = udict['id']   # 测试类id
            name  = udict['name']
        except Exception, e:
            logger.exception(e)
            self.write(err_param)
            self.finish()
            return

        query = {'class_id':cid,'name':name}
        try:
            push = yield tornado.gen.Task(
                                        mongo.find_one,
                                        DB_NAME,
                                        COLL_CLASSPUSH,
                                        query)
        except Exception, e:
            logger.exception(e)
            self.write({'code':500})
            self.finish()
            return

        res = True if push else False
        self.write({
            'code':200,
            'data':res
        })
        self.finish()


@route(r'/classpush/push', name='classpush_push')
class ClassPushPushHandler(AuthHandler):
    """ 测试类－推送 - 推送

    Stuff:
        1. 记录 推送(就是新建一个测试，绑定包到测试类)
        2. 记录 推送记录
        3. 获取并保存 包数据
    """

    @tornado.web.asynchronous
    @tornado.gen.engine
    def post(self):

        try:
            token    = self.request.headers.get('TData',{})
            udict    = json.loads(self.request.body)
            class_id = udict['class_id']   # 测试类id
            seed_id  = udict['seed_id']    # 测试包id
            name     = udict['name']
            created  = dt_str_now()
            username = token['n']
        except Exception, e:
            logger.exception(e)
            self.write(err_param)
            self.finish()
            return

        obj_id = ObjectId()
        obj_id_str = str(obj_id)

        push_dict = {
            '_id'     :obj_id,
            'class_id':class_id,
            'seed_id' :seed_id,
            'name'    :name,
            'created' :created,
            'success' :0,
            'fail'    :0,
            'devices' :0,
            'pushed'  :1
        }

        log_dict = {
            'class_id':class_id,
            'seed_id' :seed_id,
            'name'    :name,
            'created' :created,
            'username':username
        }

        try:
            # 包数据
            seed = yield tornado.gen.Task(
                                mongo.find_one,
                                DB_NAME,
                                COLL_SEED,
                                {'_id':seed_id})
            if not seed: raise ValueError('seed_404')

            # 类数据
            cls = yield tornado.gen.Task(
                                mongo.find_one,
                                DB_NAME,
                                COLL_CLASS,
                                {'_id':class_id})

            # 获取子设备数据
            dvs = yield tornado.gen.Task(
                                mongo.find_one,
                                DB_NAME,
                                COLL_CLASSDV,
                                {'classid':class_id},
                                fields={'sn_list':1})

            if not dvs:  # 该类没有子设备
                logger.info('RED__NO_DEVICES')

            #logger.info(dvs)
            sn_list = dvs.get('sn_list',[])
            sn_num = len(sn_list)

            logger.info('sn_list_len: %s' % sn_num)

            log_dict.update({
                'clsno'  :cls.get('clsno',''),
                'clsname':cls.get('name', ''),
                'keyword':cls.get('name','') + ' ' + cls.get('clsno','')
            })

            push_dict.update({
                'size'    :seed.get('bytes', 0),     # bytes
                'low'     :seed.get('low', ''),
                'high'    :seed.get('high', ''),
                'version' :seed.get('version',''),   # 内部版本号
                'url'     :seed.get('url', ''),
                'md5'     :seed.get('md5', ''),
                'ts'      :seed.get('ts', 0),
                'info'    :name,
                'content' :{'en':name},
                'max_str' :seed.get('max_str', ''),
                'finger'  :seed.get('finger', ''),
                'create_time':seed.get('created', ''),
                'class_name' :cls.get('name', ''),
                'keyword' :cls.get('name','') + ' ' + name,
                'dvs'     :sn_list,
                'devices' :sn_num,
                'fail'    :sn_num
            })

            # 获取推送返回的 _id
            # 保存推送
            yield tornado.gen.Task(
                                mongo.insert,
                                DB_NAME,
                                COLL_CLASSPUSH,
                                push_dict)

            # 保存测试类推送日志
            yield tornado.gen.Task(
                                mongo.insert,
                                DB_NAME,
                                COLL_CLASSLOG,
                                log_dict)

            # 写结果, 用来以后对比更新
            # 本来说搞个批量插入，但是想到就那么十来台测试机器，不需要了
            for sn in sn_list:
                logger.info('insert classres: %s' % sn)
                res_dict = {
                    'project':obj_id_str,
                    'scope'  :'test',
                    'sn_id'  :sn,
                    'ver'    :seed.get('version',''),
                    'status' :0,
                    'remark' :'无返回结果',
                    'created':created,
                    'cn'     :'失败',
                    'avail'  :1        # 最新的记录是可用的
                }

                yield tornado.gen.Task(
                                    mongo.insert,
                                    DB_NAME,
                                    COLL_CLASSRES,
                                    res_dict)

            # 标识测试类有推送记录
            yield tornado.gen.Task(
                                mongo.update,
                                DB_NAME,
                                COLL_CLASS,
                                {'_id':class_id},
                                {'pushed':1})

            # 标识这个包经过测试，可以在正式环境显示
            yield tornado.gen.Task(
                                mongo.update,
                                DB_NAME,
                                COLL_SEED,
                                {'_id':seed_id},
                                {'tested':1})
        except Exception, e:
            logger.exception(e)
            self.write({'code':500})
            self.finish()
            return

        self.write({'code':200})
        self.finish()


@route(r'/classpush/log', name='classpush_log')
class ClassPushLogHandler(AuthHandler):
    """ 测试类－推送 - 推送记录

    查询:
        1. 根据推送名称查询
    """

    @tornado.web.asynchronous
    @tornado.gen.engine
    def post(self):
        try:
            udict    = json.loads(self.request.body)
            cid      = udict.get('id')
            keyword  = udict.get('keyword')
            page     = udict.get('page',1)
            limit    = udict.get('page_size', 15)
        except Exception, e:
            logger.exception(e)
            self.write(err_param)
            self.finish()
            return

        skip  = (page-1)*limit
        query = {'class_id':cid}
        if keyword:
            keyword = regex_char_trans(keyword)
            query.update({'name': {'$regex':keyword}})

        try:
            sort = [['created', pymongo.DESCENDING]]
            fields = {'class_id':0,'seed_id':0}
            seeds = yield tornado.gen.Task(
                                        mongo.find_all,
                                        DB_NAME,
                                        COLL_CLASSLOG,
                                        query,
                                        skip=skip,
                                        limit=limit,
                                        sort=sort,
                                        fields=fields)
            count = yield tornado.gen.Task(
                                        mongo.count,
                                        DB_NAME,
                                        COLL_CLASSLOG,
                                        sort,
                                        query)
        except Exception, e:
            logger.exception(e)
            self.write({'code':500})
            self.finish()
            return

        pages = pages_count(count, limit)
        self.write({
            'code':200,
            'page':page,
            'page_size':limit,
            'pages':pages,
            'data':seeds
        })
        self.finish()


@route(r'/classpush/list', name='classpush_list')
class ClassPushListHandler(AuthHandler):
    """ 测试类 - 推送 - 列表 """

    @tornado.web.asynchronous
    @tornado.gen.engine
    def post(self):
        try:
            udict    = json.loads(self.request.body)
            keyword  = udict.get('keyword')
            page     = udict.get('page',1)
            limit    = udict.get('page_size', 15)
        except Exception, e:
            logger.exception(e)
            self.write(err_param)
            self.finish()
            return

        skip   = (page-1)*limit
        fields = {
            'success'   :1,
            'fail'      :1,
            'devices'   :1,
            'class_name':1,
            'created'   :1,
            'name'      :1
        }
        query = {}
        if keyword:
            keyword = regex_char_trans(keyword)
            query.update({'keyword': {'$regex':keyword}})

        try:
            sort = [['created', pymongo.DESCENDING]]
            seeds = yield tornado.gen.Task(
                                        mongo.find_all,
                                        DB_NAME,
                                        COLL_CLASSPUSH,
                                        query,
                                        fields=fields,
                                        skip=skip,
                                        limit=limit,
                                        sort=sort)

            count = yield tornado.gen.Task(
                                        mongo.count,
                                        DB_NAME,
                                        COLL_CLASSPUSH,
                                        sort,
                                        query)
        except Exception, e:
            logger.exception(e)
            self.write({'code':500})
            self.finish()
            return

        pages = pages_count(count, limit)
        self.write({
            'code'     :200,
            'page'     :page,
            'page_size':limit,
            'pages'    :pages,
            'data'     :seeds
        })
        self.finish()


@route(r'/classpush/res/list', name='classpush_res_list')
class ClassPushResHandler(AuthHandler):
    """ 测试类 - 推送－测试结果 - 列表 """

    @tornado.web.asynchronous
    @tornado.gen.engine
    def post(self):

        try:
            udict    = json.loads(self.request.body)
            push_id  = udict['push_id']
            status   = udict.get('status', -1)
            page     = udict.get('page',1)
            limit    = udict.get('page_size', 30)
        except Exception, e:
            logger.exception(e)
            self.write(err_param)
            self.finish()
            return

        skip  = (page-1)*limit
        query = {'project':push_id}
        if status >= 0: query.update({'status':status})

        try:
            sort = [['created', pymongo.DESCENDING]]
            fields = {'cn':1,'sn_id':1, 'remark':1}
            seeds = yield tornado.gen.Task(
                                        mongo.find_all,
                                        DB_NAME,
                                        COLL_CLASSRES,
                                        query,
                                        skip=skip,
                                        limit=limit,
                                        sort=sort,
                                        fields=fields)
            count = yield tornado.gen.Task(
                                        mongo.count,
                                        DB_NAME,
                                        COLL_CLASSRES,
                                        sort,
                                        query)
        except Exception, e:
            logger.exception(e)
            self.write({'code':500})
            self.finish()
            return

        pages = pages_count(count, limit)
        self.write({
            'code' :200,
            'pages':pages,
            'data' :seeds,
            'page' :page,
            'page_size':limit
        })
        self.finish()


@route(r'/classpush/res/list/down', name='classpush_res_list_down')
class ClassPushListDownHandler(RequestHandler):
    """ 测试类 - 推送－测试结果 - 列表 - 导出 """

    @tornado.web.asynchronous
    @tornado.gen.engine
    def get(self):

        try:
            push_id = self.get_argument('push_id','')
            status  = int(self.get_argument('status', -1))
            N       = int(self.get_argument('N', 999)) # excel 文件分页
        except Exception, e:
            logger.exception(e)
            self.write(err_param)
            self.finish()
            return

        filename = 'FOTA_test_'+\
                datetime.datetime.strftime(
                        datetime.datetime.now(),'%Y%m%d%H%M%S')+'.xlsx'

        query = {'project':push_id}
        if status >= 0: query.update({'status':status})

        try:
            push = yield tornado.gen.Task(
                                mongo.find_one,
                                DB_NAME,
                                COLL_CLASSPUSH,
                                {'_id':push_id})

            class_name = push.get('name', '')

            sort = [['created', pymongo.DESCENDING]]
            fields = {'cn':1,'sn_id':1, 'remark':1}
            seeds = yield tornado.gen.Task(
                                        mongo.find_all,
                                        DB_NAME,
                                        COLL_CLASSRES,
                                        query,
                                        sort=sort,
                                        fields=fields)
            logger.info('test len: %s' % len(seeds))
        except Exception, e:
            logger.exception(e)
            self.write({'code':500})
            self.finish()
            return

        seed_list = [[i['cn'],i['sn_id'],i['remark']] for i in seeds]

        # 整理数据，excel 需要的格式
        data_list = []
        seeds_array = [seed_list[i:i+N] for i in range(0, len(seed_list), N)]
        for i,s in enumerate(seeds_array):
            s.insert(0, ['结果', 'SN号', '原因'])
            data_list.append({'sheet':class_name+str(i+1),'data':s})

        excelib = Excelib(filename)
        excelib.create(data_list)
        sio = excelib.save2io()

        self.set_header('Content-Type', 'application/x-xlsx')
        self.set_header('Content-Disposition', 'attachment;filename='+filename)
        self.write(sio.getvalue())
        self.finish()


@route(r'/classpush/num', name='classpush_num')
class ClassPushNumHandler(AuthHandler):
    """ 测试类 - 推送 - 更新数量数据 """

    @tornado.web.asynchronous
    @tornado.gen.engine
    def post(self):
        try:
            udict    = json.loads(self.request.body)
            push_id  = udict.get('push_id')
        except Exception, e:
            logger.exception(e)
            self.write(err_param)
            self.finish()
            return

        query = {'_id':push_id}
        try:
            fields = {'devices':1,'success':1,'fail':1}
            seed = yield tornado.gen.Task(
                                        mongo.find_one,
                                        DB_NAME,
                                        COLL_CLASSPUSH,
                                        query,
                                        fields=fields)
            seed.update({'now':dt_str_now()})
        except Exception, e:
            logger.exception(e)
            self.write({'code':500})
            self.finish()
            return

        self.write({
            'code':200,
            'data':seed
        })
        self.finish()



